package com.example.data.permission.mp.interceptor;

/**
 * Created by TD on 2021/7/22
 * 使用Mybatis 原生插件实现数据权限
 */

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.example.data.permission.mp.permission.DataPermission;
import com.example.data.permission.mp.permission.DataPermissionType;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.Connection;

/**
 * Created by TD on 2021/7/21
 * mybatis 数据权限处理器,拦截所有的编译SQL方法执行
 */
@Intercepts(
        {@Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )})
@Slf4j
@Component
@ConditionalOnProperty(prefix = "jxc.data.permission", name = "enabled", havingValue = "true")
public class MyBatisDataPermissionInterceptor implements Interceptor {

    @Autowired
    MyDataPermissionHandler myDataPermissionHandler;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 获取StatementHandler、DataPermission注解信息
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // 2. 不是查询操作放行
        if (SqlCommandType.SELECT != ms.getSqlCommandType() || StatementType.CALLABLE == ms.getStatementType()) {
            return invocation.proceed();
        }
        String mappedStatementId = ms.getId();// 获取mapper接口及方法名 com.example.data.permission.dao.BsdAccountMapper.selectAccountsByUser
        String className = mappedStatementId.substring(0, mappedStatementId.lastIndexOf(StringPool.DOT));// 接口类名：com.example.data.permission.dao.BsdAccountMapper
        String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(StringPool.DOT) + 1);// 方法名：selectAccountsByUser
        DataPermission dataPermission = null;
        Method[] methods = Class.forName(className).getDeclaredMethods();
        Class.forName(className).getMethod("1111");
        for (Method md : methods) {
            if (methodName.equalsIgnoreCase(md.getName())) {
                dataPermission = md.getAnnotation(DataPermission.class);
            }
        }
        if (dataPermission == null) {
            // 3. Mapper接口没有注解，直接放行
            return invocation.proceed();
        }
        // 4. 获取注解规则
        DataPermissionType dataPermissionType = dataPermission.type();
        // 5. ALL 放行
        if (dataPermissionType == DataPermissionType.ALL) {
            return invocation.proceed();
        }
        // 6. 原始SQL
        String originalSql = ((String) metaObject.getValue("delegate.boundSql.sql")).replaceAll("\\n", StrUtil.EMPTY).replaceAll("\\t", StrUtil.EMPTY);
        // 7. 按照门店in 处理
        String execSql = "";
        if (dataPermissionType == DataPermissionType.OWN_SHOP_CHILD || dataPermissionType == DataPermissionType.OWN_SHOP) {
            execSql = myDataPermissionHandler.getSqlSegment(originalSql, dataPermission);
        } else if (dataPermission.type() == DataPermissionType.SYS_CUSTOM) {
            // 8. 自定义数据权限处理
            execSql = myDataPermissionHandler.getSqlSegment(originalSql, ms, statementHandler, dataPermission);
        }
        metaObject.setValue("delegate.boundSql.sql", execSql);
        return invocation.proceed();
    }

}